import { Link, TypescriptOnly } from '@brillout/docpress'
import { UiFrameworkExtension } from '../../components'
import { TableStyle } from './TableStyle'

Use following settings to set `<head>` tags (aka document metadata).

<TableStyle>
<table>
  <thead>
    <tr>
      <th>Setting</th>
      <th>HTML `<head>` tags</th>
    </tr>
  </thead>
  <tbody>
    <tr>
      <td><Link href="/title">`+title`</Link></td>
      <td>
        ```html
        <title>
        <meta property="og:title">
        ```
      </td>
    </tr>
    <tr>
      <td><Link href="/description">`+description`</Link></td>
      <td>
        ```html
        <meta name="description">
        <meta property="og:description">
        ```
      </td>
    </tr>
    <tr>
      <td><Link href="/image">`+image`</Link></td>
      <td>
        Preview image upon sharing URLs:
        ```html
        <meta property="og:image">
        <meta name="twitter:card" content="summary_large_image">
        ```
      </td>
    </tr>
    <tr>
      <td><Link href="/viewport">`+viewport`</Link></td>
      <td>
        For mobile responsive design:
        ```html
        <meta name="viewport">
        ```
      </td>
    </tr>
    <tr>
      <td><Link href="/Head">`+Head`</Link></td>
      <td>
        Other `<head>` tags, for example:
        ```html
        <link rel="icon"> <!-- Favicon -->
        <link rel="apple-touch-icon"> <!-- Mobile icon -->
        <script></script>
        <!-- ... -->
        ```
      </td>
    </tr>
  </tbody>
</table>
</TableStyle>

> See <Link href="/settings#html-shell" doNotInferSectionTitle /> for other settings that modify the HTML shell.

> These settings are provided by <UiFrameworkExtension name /> — if you aren't using <UiFrameworkExtension succinct /> then see <Link href="/head-manual" /> instead.


## Global / per-page

You can set `<head>` tags that apply globally to all pages:

```ts
// pages/+config.ts
// Environment: config

import type { Config } from 'vike/types'
import image from './previewImage.jpg'

// Applies to all pages (can be overridden)
export default {
  // Default <title>
  title: 'Awesome Rockets',
  // Default <meta name="description">
  description: 'We deliver payload to space',
  // Default <meta property="og:image">
  image
} satisfies Config
```

```tsx
// pages/+Head.ts
// Environment: server

import favicon from './favicon.png'

// Applies to all pages (cannot be overridden)
export function Head() {
  return (
    <>
      {/* Icon shown in the browser tab (aka favicon) */}
      <link rel="icon" href={favicon} type="image/svg+xml" />
    </>
  )
}
```

You can also set `<head>` tags only for a single page or a group of pages:

```ts
// pages/movies/+config.ts
// Environment: config

import type { Config } from 'vike/types'
import image from './previewImage.jpg'

// Overrides the defaults defined above
export default {
  title: 'Movies',
  description: 'List of movies.',
  image
} satisfies Config
```

```tsx
// pages/movies/+Head.ts
// Environment: server

import iconMobile from './iconMobile.jpg'

// This doesn't override the favicon defined at /pages/+Head.ts above
export function Head() {
  return (
    <>
      {/* Icon shown on mobile homescreens (PWA) */}
      <link rel="apple-touch-icon" href={iconMobile} type="image/svg+xml" />
    </>
  )
}
```

While `+title`, `+description`, and `+image` can be overridden (they aren't cumulative), the `+Head` setting is cumulative and cannot be overridden — see <Link href="/Head#cumulative"></Link>.

> See also: <Link href="/config#inheritance"/>


## Data

You can set `<head>` tags based on <Link href="/data-fetching">fetched data</Link>, using either:
 - `useConfig()`, or
 - `pageContext` functions.

<div style={{height:8}}/>

**`useConfig()`**

You can use the <Link href="/useConfig">`useConfig()` hook</Link> to set `<head>` tags dynamically inside your `+data` hook.

```ts
// pages/movies/+data.ts
// Environment: server

export { data }
export type Data = Awaited<ReturnType<typeof data>>

import { useConfig } from 'vike-react/useConfig' // or vike-{vue,solid}
import type { PageContextServer } from 'vike/types'

async function data(pageContext: PageContextServer) {
  const config = useConfig()

  const response = await fetch('https://star-wars.brillout.com/api/films.json')
  let { movies } = await response.json()

  config({
    title: `${movies.length} movies`,
    description: `List of all ${movies.length} Star Wars movies.`
  })

  return { movies }
}
```

> Make sure to call `useConfig()` before any `await`:
> ```ts
> async function data() {
>   const response = await fetch('https://star-wars.brillout.com/api/films.json')
>   // ❌ Doesn't work: useConfig() must be called before `await fetch()`
>   const config = useConfig()
> }
> ```

> You can also use it inside UI components, see <Link href="/useConfig#ui-components" doNotInferSectionTitle />.

<div style={{height:28}}/>

**`pageContext` functions**

You can set `+title`, `+description` and `+image` using a <Link href="/pageContext">`pageContext`</Link> function.

```ts
// pages/movies/+title.ts
// Environment: server, client

import type { PageContext } from 'vike/types'
import type { Data } from './+data'

export default (pageContext: PageContext<Data>) => `${pageContext.data.movies.length} movies`
```

```ts
// pages/movies/+data.ts
// Environment: server

export { data }
export type Data = Awaited<ReturnType<typeof data>>

async function data() {
  const response = await fetch('https://star-wars.brillout.com/api/films.json')
  let { movies } = await response.json()
  return { movies }
}
```

You can define some default logic that applies to all pages:

```ts
// pages/+title.ts
// Environment: server, client

import type { PageContext } from 'vike/types'

type Data = { title?: string } | undefined

// Applies to all pages: if a page fetches data and data.title is defined then
// use it to set the page's title.
export default (pageContext: PageContext<Data>) => pageContext.data?.title || 'Some Default Title'
```

The `+Head` setting is a UI component — you can use <Link href="/useData">`useData()`</Link> and <Link href="/usePageContext">`usePageContext()`</Link> as usual:

```tsx
// pages/movie/@id/+Head.ts
// Environment: server

import { useData } from 'vike-react/useData' // or vike-{vue,solid}
import type { Data } from './+data'

export function Head() {
  const data = useData<Data>()
  return (
    <>
      {/* Image shown when sharing on social sites (Twitter, WhatsApp, ...) */}
      <meta property="og:image" content={data.movie.image} />
    </>
  )
}
```


## SPA

While the content of your SPA pages isn't included in the HTML, all head settings are:

```html
<html>
  <!-- ✅ All head settings of the SPA page are included in the HTML -->
  <head>
    <title>AwesomeRockets</title>
    <meta name="description" content="The rocket company.">
    <meta property="og:image" content="/assets/previewImage.9d8ha1.jpg">
    <meta name="twitter:card" content="summary_large_image">
    <meta name="viewport" content="width=1200" />
  </head>

  <!-- ❌ The content of the SPA page isn't included in the HTML -->
  <div id="root"></div>

  <!-- The client-side JavaScript of the SPA page -->
  <script src="/spa-entry.js" type="module"></script>
</html>
```

> See also:
> - <Link href="/SSR-vs-SPA" />
> - <Link href="/what-is-SSR-and-SPA" />


## Custom settings

You can create your own custom settings.

> You can also create custom components hooks, see for example [`vike-metadata`](https://github.com/Blankeos/vike-metadata).

For example, in the following, we create a new setting `+dynamicFavicon` that allows different favicons to be set for different pages. (Note that `+Head` can only be used for setting a global favicon, see <Link href="/Head#only-html" />.)

### Setting creation

```ts
// pages/+config.ts
// Environment: config

import type { Config } from 'vike/types'

export default {
  meta: {
    dynamicFavicon: {
      env: { server: true, client: true }
    }
  }
} satisfies Config
```
> See: <Link href="/meta" />

```tsx
// pages/+Head.ts
// Environment: server

import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}

export default () => {
  const pageContext = usePageContext()
  const { dynamicFavicon } = pageContext.config
  return <>{dynamicFavicon && <link rel="icon" href={dynamicFavicon} type="image/svg+xml" />}</>
}
```
```ts
// pages/+onAfterRenderClient.ts
// Environment: client

import type { PageContextClient } from 'vike/types'

export default (pageContext: PageContextClient) => {
  if (!pageContext.isHydration) {
    const { dynamicFavicon } = pageContext.config
    updateFavicon(dynamicFavicon)
  }
}

// https://stackoverflow.com/questions/260857/changing-website-favicon-dynamically
function updateFavicon(dynamicFavicon) {
  let link = document.querySelector("link[rel~='icon']")
  if (!dynamicFavicon) {
    if (link) document.head.removeChild(link)
    return
  }
  if (!link) {
    link = document.createElement('link')
    link.rel = 'icon'
    document.head.appendChild(link)
  }
  link.href = dynamicFavicon
}
```

<TypescriptOnly>

```ts ts-only
// pages/+config.ts
// Environment: config

declare global {
  namespace Vike {
    interface Config {
      dynamicFavicon?: string
    }
  }
}
```

> See: <Link href="/meta#typescript" />

</TypescriptOnly>

### Setting usage

```ts
// pages/+config.ts
// Environment: config

import type { Config } from 'vike/types'
import favicon from './favicon.svg'

export default {
  // Default favicon
  dynamicFavicon: favicon
} satisfies Config
```

```ts
// pages/premium-members/+config.ts
// Environment: config

import type { Config } from 'vike/types'
import favicon from './favicon.svg'

export default {
  // Favicon for /premium-members
  dynamicFavicon: favicon
} satisfies Config
```

## Internationalization

Example of internationalizing (i18n) `<head>` tags:

```tsx
// pages/movies/+Head.ts
// Environment: server

export { Head }

import { usePageContext } from 'vike-react/usePageContext' // or vike-{vue,solid}

function Head() {
  const pageContext = usePageContext()
  const description =
    pageContext.locale === 'de-DE' ? 'Liste von Star Wars Filme.' : 'List of Star Wars Movies.'

  return (
    <>
      <meta name="description" content={description} />
    </>
  )
}
```
```ts
// pages/movies/+title.ts
// Environment: server, client

import type { PageContext } from 'vike/types'

export function title(pageContext: PageContext) {
  const title = pageContext.locale === 'de-DE' ? 'Star Wars Filme' : 'Star Wars Movies'

  return title
}
```

See also:
 - <Link href="/i18n" />
 - <Link href="/usePageContext" />


## Markdown

See <Link href="/markdown#metadata" />.


## See also

- <Link href="/settings#html-shell" doNotInferSectionTitle />
- <Link href="/useConfig" />
- [`vike-metadata`](https://github.com/Blankeos/vike-metadata)
- [Blog post "My current HTML boilerplate" showcasing and explaining a common boilerplate](https://www.matuzo.at/blog/html-boilerplate/)
- <Link href="/head-manual" />
